/*
 * This file is part of the Refran Library
 * Copyright (C) 2009 César Enrique García
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author:  $
 * $Date:  $
 * $Revision: $
 * $Name:  $
 */

#include <ltdl.h>
#include <boost/filesystem.hpp>
#include "Error.h"
#include "RecipeManagerLocalShlib.h"

Refran::RecipeManagerLocalShlib::RecipeManagerLocalShlib()
{
}

Refran::RecipeManagerLocalShlib::~RecipeManagerLocalShlib()
{
}

void Refran::RecipeManagerLocalShlib::addShlibPath(const std::string& path)
{
    if(path.empty())
        throw Error(__func__,"The given recipe path is empty");
    std::lock_guard<std::mutex> guard(m_pathMutex);
    m_shlibPaths.push_back(path);
    m_pathHasBeenSearched.push_back(false);
    return;
}

void Refran::RecipeManagerLocalShlib::clearShlibPaths()
{
    std::lock_guard<std::mutex> guard(m_pathMutex);
    m_shlibPaths.clear();
    m_pathHasBeenSearched.clear();
    m_recipeNameVersionLibpath.clear();
    return;
}

void Refran::RecipeManagerLocalShlib::setShlibPaths
(const std::vector<std::string>& paths)
{
    for(auto path = paths.begin() ; path != paths.end(); ++path)
        if(path->empty())
            throw Error(__func__,"At least one of the given recipe paths is empty");
    std::lock_guard<std::mutex> guard(m_pathMutex);
    m_shlibPaths = paths;
    m_pathHasBeenSearched.clear();
    m_pathHasBeenSearched.assign(paths.size(), false);
    m_recipeNameVersionLibpath.clear();
    return;
}

std::vector<std::string>
Refran::RecipeManagerLocalShlib::getAvailableRecipeNames()
{
    std::lock_guard<std::mutex> guard(m_pathMutex);
    scanPathForRecipes_();

    std::vector<std::string> recipeList;

    auto iter = m_recipeNameVersionLibpath.begin();
    for( ; iter!=m_recipeNameVersionLibpath.end(); ++iter)
        recipeList.push_back(iter->first);

    return recipeList;
}

void
Refran::RecipeManagerLocalShlib::registerRecipe(const std::string& recipeName)
{
    std::lock_guard<std::mutex> guard(m_pathMutex);
    scanPathForRecipes_();

    auto iter = m_recipeNameVersionLibpath.find(recipeName);

    if(iter != m_recipeNameVersionLibpath.end())
    {
        auto versionLibpathMap  = iter;
        auto latestVersion = versionLibpathMap->rbegin();
        const std::string& libpath = latestVersion->second;
        
        Libtool::getSymbol(libpath);
        Libtool::getSymbol("registerRefranRecipe");
http://www.gnu.org/software/libtool/manual/libtool.html#Using-libltdl
http://www.gnu.org/software/libtool/manual/libtool.html#Dlopened-modules
http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html
//        void *dlHandler   = lt_dlopen(libpath, RTLD_NOW);
//        if(dlHandler == NULL)
//            throw Error(__func__,"Could not open library '"+libpath+"' (dlerror: "+dlerror());
//        void *registrator = dlsym(dlHandler, "registerRefranRecipe");
//        if(registrator == NULL)
//            throw;

        //  if(plugin.get_symbol("registerRecipe", (void*)registerRecipe))
        //register_recipe();
    }
    else
        throw Refran::Error(__func__,"Could not register Recipe '"+recipeName+"'. "
                            "It is not in plugin search path '"+recipeName+"'.");

    return;
}

void Refran::RecipeManagerLocalShlib::scanPathForRecipes_()
{
    namespace fs = boost::filesystem;

    if(m_shlibPaths.empty())
        throw Error(__func__,"No plugin path has been set");

    for(int i = 0; i < m_shlibPaths.size(); ++i)
    {
        if(!m_pathHasBeenSearched[i])
        {
            fs::path shlibPath(m_shlibPaths[i]);

            // default construction yields past-the-end
            fs::recursive_directory_iterator end_itr;
            for(fs::recursive_directory_iterator itr( shlibPath );
                            itr != end_itr; ++itr)
            {

                //Glib::Module plugin(itr->leaf());
                int (*registerRecipe)();
                // 	if(plugin.get_symbol("registerRecipe", (void*)registerRecipe))
                // 		plugins.push_back(itr->path() / itr->leaf);
                {
                    //Do the hard stuff here;
                    throw Error(__func__,"Not implemented");

                }
            }
            m_pathHasBeenSearched[i] = true;
        }
    }
}
